package de.fhwf.mixedreality.client;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import static java.lang.Math.*;
import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class AgentView extends JPanel implements IView {
	private static final long serialVersionUID = 1L;
	private static final String SELF_IMAGE = "../jMRClient/resources/me.png";
	private static final String TEAMMATE_IMAGE = "../jMRClient/resources/teammate.png";
	private static final String OPPONENT_IMAGE = "../jMRClient/resources/opponent.png";
	private static final String MARKER_IMAGE = "../jMRClient/resources/marker.png";
	private static final String CORNER_MARKER_IMAGE = "../jMRClient/resources/marker2.png";
	private static final String POLE_MARKER_IMAGE = "../jMRClient/resources/polemarker.png";
	private static final String BALL_IMAGE = "../jMRClient/resources/ball.png";
	private static final String FONT_NAME = "technicalregular";
	private static final int FONT_SIZE = 17;
	protected int mWidth;
	protected int mHeight;
	protected double mScale;
	private Image mBackBuffer;
	protected BufferedImage mImageSelf;
	protected BufferedImage mImageTeammate;
	protected BufferedImage mImageOpponent;
	private BufferedImage mImageMarker;
	protected BufferedImage mImageCornerMarker;
	protected BufferedImage mImagePoleMarker;
	protected BufferedImage mImageBall;
	private Color mBackgroundColor; // Color of the screen background
	private Color mFontFGColor; // Font foreground color
	private Color mFontSHColor; // Font shadow color

	public AgentView(int width, int height) {
		
		mWidth = width;
		mHeight = height;
		// Init font colors
		mFontFGColor = Color.WHITE;
		mFontSHColor = Color.BLACK;
		// Init background color
		mBackgroundColor = new Color(0x00, 0xbb, 0x88);
		// Load the images from the files
		mImageSelf = loadImage(SELF_IMAGE);
		mImageTeammate = loadImage(TEAMMATE_IMAGE);
		mImageOpponent = loadImage(OPPONENT_IMAGE);
		mImageMarker = loadImage(MARKER_IMAGE);
		mImageCornerMarker = loadImage(CORNER_MARKER_IMAGE);
		mImagePoleMarker = loadImage(POLE_MARKER_IMAGE);
		mImageBall = loadImage(BALL_IMAGE);
		// Init scale
		mScale = 0.6;
		// Disable layout manager
		// Add display panel
		
		this.setBounds(0, 0, mWidth, mHeight);
		// Show window
		// Create BackBuffer
		createBackBuffer();
		// Load font
		Graphics g = mBackBuffer.getGraphics();
		g.setFont(new Font(FONT_NAME, Font.PLAIN, FONT_SIZE));
		g.dispose();
		this.setVisible(true);
	}

	/** Draws a the one's self */
	public void drawSelf(Vector self, String label) {
		drawItem(self, label, mImageSelf);
	}

	/** Draws a teammate in the given polar coordinates */
	public void drawTeammate(Vector teammate, String label) {
		drawItem(teammate, label, mImageTeammate);
	}

	/** Draws an opponent in the given polar coordinates */
	public void drawOpponent(Vector opponent, String label) {
		drawItem(opponent, label, mImageOpponent);
	}

	/** Draws a field marker in the given polar coordinates */
	public void drawMarker(Vector marker, String label) {
		drawItem(marker, label, mImageMarker);
	}

	/** Draws a ball in the given polar coordinates */
	public void drawBall(Vector ball, String label) {
		drawItem(ball, label, mImageBall);
	}
	
	
	//Draws a Circle around the bot
	public void drawCircleAroundBot(Vector vectorBot, int radius){
		int x = (mWidth / 2 + (int) (vectorBot.getLength() * mScale * Math.sin(vectorBot.getAngle())))- (radius/2);
		
		int y = (mHeight / 2 + (int) (vectorBot.getLength() * mScale * Math.cos(vectorBot.getAngle())))- (radius/2);
		
		drawCircle(x, y, radius);
	}
	
	
	//Draws a ArcCircle around the bot
	public void drawArcCircleAroundBot(Vector vectorBot, Vector vectorTarget, int angle){
		
		int radius = (int) ( vectorTarget.getLength()* mScale*2);
		
		int x = mWidth / 2 + (int) (vectorBot.getLength() * mScale * Math.sin(vectorBot.getAngle())) - (radius/2);
		
		int y = mHeight / 2 + (int) (vectorBot.getLength() * mScale * Math.cos(vectorBot.getAngle())) - (radius/2);
		
		int startAngle = -(int) toDegrees(vectorTarget.getAngle()) - ((angle/2)-90);
		
		Vector vector1 = new Vector(toRadians(startAngle+90), vectorTarget.getLength());
		
		Vector vector2 = new Vector(toRadians(startAngle+angle+90), vectorTarget.getLength());
		
		drawArcCircle(x, y, radius, startAngle, angle, vector1, vector2, vectorBot);
	}

	/** Draws a white arrow representing the Vector in white w-th shadow */
	public void drawVectorFromVector(Vector fromVector, Vector toVector) {
		// Calculates the coordinates based on the polar vector given (angle
		// zero is top)
		int x = mWidth
				/ 2
				+ (int) (fromVector.getLength() * mScale * Math.sin(fromVector
						.getAngle()));
		int y = mHeight
				/ 2
				- (int) (fromVector.getLength() * mScale * Math.cos(fromVector
						.getAngle()));
		drawPolarVectorWithShadow(x, y, toVector, "", Color.WHITE);
	}

	/**
	 * Draws a white arrow representing the Vector in white with shadow and
	 * augments it with a caption
	 */
	public void drawVectorFromVector(Vector fromVector, Vector toVector,
			String label) {
		// Calculates the coordinates based on the polar vector given (angle
		// zero is top)
		int x = mWidth
				/ 2
				+ (int) (fromVector.getLength() * mScale * Math.sin(fromVector
						.getAngle()));
		int y = mHeight
				/ 2
				- (int) (fromVector.getLength() * mScale * Math.cos(fromVector
						.getAngle()));
		drawPolarVectorWithShadow(x, y, toVector, label, Color.WHITE);
	}

	/**
	 * Draws a white arrow representing the Vector in white with shadow and
	 * augments it with a caption
	 */
	public void drawVectorFromVector(Vector fromVector, Vector toVector,
			String label, Color color) {
		// Calculates the coordinates based on the polar vector given (angle
		// zero is top)
		int x = mWidth
				/ 2
				+ (int) (fromVector.getLength() * mScale * Math.sin(fromVector
						.getAngle()));
		int y = mHeight
				/ 2
				- (int) (fromVector.getLength() * mScale * Math.cos(fromVector
						.getAngle()));
		drawPolarVectorWithShadow(x, y, toVector, label, color);
	}

	/** Draws a white arrow representing the Vector in white with shadow */
	public void drawVectorFromMe(Vector toVector) {
		// Calculates the coordinates based on the polar vector given (angle
		// zero is top)
		int x = mWidth / 2;
		int y = mHeight / 2;
		drawPolarVectorWithShadow(x, y, toVector, "", Color.WHITE);
	}

	/**
	 * Draws a white arrow representing the Vector in white with shadow and
	 * augments it with a caption
	 */
	public void drawVectorFromMe(Vector toVector, String label) {
		// Calculates the coordinates based on the polar vector given (angle
		// zero is top)
		int x = mWidth / 2;
		int y = mHeight / 2;
		drawPolarVectorWithShadow(x, y, toVector, label, Color.WHITE);
	}

	/**
	 * Draws a white arrow representing the Vector in white with shadow and
	 * augments it with a caption
	 */
	public void drawVectorFromMe(Vector toVector, String label, Color color) {
		// Calculates the coordinates based on the polar vector given (angle
		// zero is top)
		int x = mWidth / 2;
		int y = mHeight / 2;
		drawPolarVectorWithShadow(x, y, toVector, label, color);
	}

	/** Updates the screen */
	public void update(IClient client) {
		this.repaint();
	}

	/** Clear the screen */
	public void clear() {
		Graphics g = mBackBuffer.getGraphics();
		g.setColor(mBackgroundColor);
		g.fillRect(0, 0, mWidth, mHeight);
		g.dispose();
	}

	/** Draws all objects in the client's world */
	public void drawClientWorld(IClient client) {
	
		IPositionsStorage pos = ((IClient) client).getPositions();
		// Clear the agent view screen
		clear();
		// Board Lines
		drawBoardLine(pos.myCornerFlag1(), pos.myCornerFlag2(), 5);
		drawBoardLine(pos.myCornerFlag2(), pos.opCornerFlag2(), 5);
		drawBoardLine(pos.opCornerFlag1(), pos.opCornerFlag2(), 5);
		drawBoardLine(pos.opCornerFlag1(), pos.myCornerFlag1(), 5);
		// Draws the stuff in the agent view
		drawSelf(pos.me(), "myid_" + client.myID());
		for (int i = 0; i < IClient.MAX_PLAYERS; i++) {
			if (client.isTeammateFound(i)) {
				drawTeammate(pos.teammate(i), "tm_" + i);
			}
			if (client.isOpponentFound(i)) {
				drawOpponent(pos.opponent(i), "op_" + i);
			}
		}
		drawItem(pos.myGoalPole1(), "mygoal_pole1", mImagePoleMarker);
		drawItem(pos.myGoalPole2(), "mygoal_pole2", mImagePoleMarker);
		drawItem(pos.opGoalPole1(), "opgoal_pole1", mImagePoleMarker);
		drawItem(pos.opGoalPole2(), "opgoal_pole2", mImagePoleMarker);
		drawItem(pos.myCornerFlag1(), "mycorner_flag1", mImageCornerMarker);
		drawItem(pos.myCornerFlag2(), "mycorner_flag2", mImageCornerMarker);
		drawItem(pos.opCornerFlag1(), "opcorner_flag1", mImageCornerMarker);
		drawItem(pos.opCornerFlag2(), "opcorner_flag2", mImageCornerMarker);
		drawBall(pos.ball(), ""); // ball
	}

	private BufferedImage loadImage(String name) {
		File imageFile = null;
		BufferedImage img = null;
		try {
			imageFile = new File(name).getCanonicalFile();
			img = ImageIO.read(imageFile);
			return img;
		} catch (IOException e) {
			if (imageFile != null) {
				System.err.println("[AgentView] loadImage: Image '"
						+ imageFile.getAbsolutePath() + "' not found!");
			} else {
				System.err.println("[AgentView] loadImage: Image '" + name
						+ "' not found!");
			}
		}
		return null;
	}

	private void createBackBuffer() {
		 mBackBuffer = new BufferedImage(mWidth, mHeight,BufferedImage.TYPE_INT_ARGB);
	}

	// Pastes an image centered into the x,y coordinates of the backbuffer
	protected void drawImage(int x, int y, Image source) {
		if (source != null) {
			// Give the coordinates centered
			int offsetx = x - source.getWidth(null) / 2;
			int offsety = y - source.getHeight(null) / 2;

			// Paste the source image into the destination
			Graphics g = mBackBuffer.getGraphics();
			g.drawImage(source, offsetx, offsety, null);
			g.dispose();
		}
	}

	// Draws some text at the given coordinates
	public void drawText(int x, int y, String text) {
		Graphics g = mBackBuffer.getGraphics();
		g.setColor(mFontSHColor);
		g.drawString(text, x + 1, y + 1);
		g.setColor(mFontFGColor);
		g.drawString(text, x, y);
		g.dispose();
	}

	/**
	 * Draws some text at the given coordinates
	 * 
	 * @param x
	 * @param y
	 * @param text
	 * @param color
	 */
	public void drawText(int x, int y, String text, Color color) {
		Graphics g = mBackBuffer.getGraphics();
		g.setColor(mFontSHColor);
		g.drawString(text, x + 1, y + 1);
		g.setColor(color);
		g.drawString(text, x, y);
		g.dispose();
	}

	// Draw an item in the given position
	protected void drawItem(Vector position, String label, Image image) {
		if (position != null) {
			// Calculates the coordinates based on the polar vector given (angle
			// zero is top)
			int x = mWidth
					/ 2
					+ (int) (position.getLength() * mScale * Math.sin(position
							.getAngle()));
			int y = mHeight
					/ 2
					- (int) (position.getLength() * mScale * Math.cos(position
							.getAngle()));

			// Draw the item
			drawImage(x, y, image);

			// Draw the text
			drawText(x, y, label);
		}
	}
	
	//Draws a circle
	public void drawCircle(int cirX, int cirY, int cirRadius){
		Color c1 = new Color(0xff, 0xff, 0xff);
		Graphics g = mBackBuffer.getGraphics();
		g.setColor(c1);
		g.drawOval(cirX, cirY, cirRadius, cirRadius);
		g.dispose();
	}

	//Draws a ArcCircle
	public void drawArcCircle(int recX, int recY, int radius, int startAngle, int angle, Vector vector1, Vector vector2, Vector vectorBot){		
	
		int x = mWidth / 2 + (int) (vectorBot.getLength() * mScale * Math.sin(vectorBot.getAngle()));
		int y = mHeight / 2 + (int) (vectorBot.getLength() * mScale * Math.cos(vectorBot.getAngle()));
		
		int xLine1 = mWidth / 2 + (int) (vector1.getLength() * mScale * Math.sin(vector1.getAngle()));
		int yLine1 = mHeight / 2 + (int) (vector1.getLength() * mScale * Math.cos(vector1.getAngle()));
		
		int xLine2 = mWidth / 2 + (int) (vector2.getLength() * mScale * Math.sin(vector2.getAngle()));
		int yLine2 = mHeight / 2 + (int) (vector2.getLength() * mScale * Math.cos(vector2.getAngle()));
				
		Graphics g = mBackBuffer.getGraphics();
		g.drawArc(recX, recY, radius, radius, startAngle, angle);
		g.drawLine(x, y, xLine1, yLine1);
		g.drawLine(x, y, xLine2, yLine2);
		g.setColor(Color.RED);
		
		//Filled ArcCirlce with transparence
//		Graphics2D g2 = (Graphics2D) g;
//		g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,(float) 0.5 ));
//		g2.fillArc(recX, recY, radius, radius, startAngle, angle);
		
		g.dispose();
	}
	
	

	// Draws a polar vector, an arrow
	private void drawPolarVector(int fromx, int fromy, double radius,
			double angle, Color color) {
		int arrowtipx = fromx + (int) (radius * mScale * Math.sin(angle));
		int arrowtipy = fromy - (int) (radius * mScale * Math.cos(angle));
		Graphics g = mBackBuffer.getGraphics();
		g.setColor(color);
		g.drawLine(fromx, fromy, arrowtipx, arrowtipy);
		g.drawLine(arrowtipx, arrowtipy,
				(int) ((double) arrowtipx + 15.0 * Math.sin(angle + Math.PI
						- 0.4)), (int) ((double) arrowtipy - 15.0 * Math
						.cos(angle + Math.PI - 0.4)));
		g.drawLine(arrowtipx, arrowtipy,
				(int) ((double) arrowtipx + 15.0 * Math.sin(angle + Math.PI
						+ 0.4)), (int) ((double) arrowtipy - 15.0 * Math
						.cos(angle + Math.PI + 0.4)));
		g.dispose();
	}

	// Draws a white arrow representing the vector_t in white with shadow
	private void drawPolarVectorWithShadow(int fromx, int fromy, Vector vector,
			String label, Color color) {
		drawPolarVector(fromx + 1, fromy + 1, vector.getLength(), vector
				.getAngle(), Color.BLACK);
		drawPolarVector(fromx, fromy, vector.getLength(), vector.getAngle(),
				color);
		// Draw the text
		int tox = mWidth
				/ 2
				+ (int) (vector.getLength() * mScale * Math.sin(vector
						.getAngle()));
		int toy = mHeight
				/ 2
				- (int) (vector.getLength() * mScale * Math.cos(vector
						.getAngle()));
		drawText((fromx + tox) / 2, (fromy + toy) / 2, label);
	}

	protected void drawBoardLine(Vector from, Vector to, int fat) {
		Graphics g = mBackBuffer.getGraphics();
		g.setColor(new Color(255, 255, 255));
		int x1 = mWidth / 2
				+ (int) (from.getLength() * mScale * Math.sin(from.getAngle()));
		int y1 = mHeight / 2
				- (int) (from.getLength() * mScale * Math.cos(from.getAngle()));
		int x2 = mWidth / 2
				+ (int) (to.getLength() * mScale * Math.sin(to.getAngle()));
		int y2 = mHeight / 2
				- (int) (to.getLength() * mScale * Math.cos(to.getAngle()));
		g.drawLine(x1, y1, x2, y2);
		g.dispose();
	}

	public void addOverlay(JPanel overlay, int x, int y, int width, int height) {
		addOverlay(overlay, x, y, width, height, 0.75f);
	}

	public void addOverlay(JPanel overlay, int x, int y, int width, int height,
			float opacity) {
		JPanel over = new JPanel() {
			private static final long serialVersionUID = 1L;

			public void paint(Graphics g) {
				Graphics2D g2 = (Graphics2D) g.create();
				g2.setComposite(AlphaComposite.getInstance(
						AlphaComposite.SRC_OVER, Float.parseFloat(this
								.getName())));
				super.paint(g2);
				g2.dispose();
			}
		};
		over.setName(Float.toString(opacity));
		over.setLayout(null);
		over.add(overlay);
		overlay.setBounds(0, 0, width, height);
		this.add(over, new Integer(1));
		over.setBounds(x, y, width, height);
		over.setOpaque(false);
		this.setVisible(true);
	}
	
	public void paint(Graphics g) {
		if (g != null) { // component already visible?
			// is there a backBuffer to draw?
			if (mBackBuffer != null) {
				g.drawImage(mBackBuffer, 0, 0, null);
			} else {
				// if not, create one
				createBackBuffer();
			}
		}
	}
	
	public void setScale(double scale){
		this.mScale = scale;
	}
}